package ue

import (
	"crypto/x509"
	"encoding/pem"
	"errors"
	"fmt"
	"os"
	"strconv"
	"strings"

	"github.com/panjf2000/gnet/v2"
	log "github.com/sirupsen/logrus"
	"google.golang.org/protobuf/proto"

	"cybertwin/pb/model"
	"cybertwin/utils/ca"
	"cybertwin/utils/cryptotools"
	"cybertwin/utils/generator"
	"cybertwin/utils/protocol"
	"cybertwin/utils/stringutils"
)

func (client *RegistryClient) RegisterUser(conn gnet.Conn) (err error) {
	// 注册协议封装

	registerParam := &model.UserRegisterParam{
		Username: client.Username,
	}
	codec := protocol.AccessProtocolCodec{}
	marshal, err := proto.Marshal(registerParam) // 序列化请求参数
	if err != nil {
		log.Errorln("[RegisterUser]-序列化registerParam失败:", err.Error())
		return
	}
	encode, err := codec.EncryptEncode(marshal, protocol.TYPE_USER_REGISTER) // 生成协议
	if err != nil {
		log.Errorln("[RegisterUser]-EncryptEncode registerParam失败:", err.Error())
		return
	}

	size, err := conn.Write(encode)
	if err != nil {
		log.Errorln("[RegisterUser]-向注册中心write数据失败:", err.Error())
		return
	}
	log.Infof("[RegisterUser-发送注册请求，%dbytes数据", size)

	return
}

func (client *RegistryClient) RegisterUE(conn gnet.Conn) (err error) {
	// 设备注册协议封装
	ueType := generator.GenRandomUeType()
	file, err := os.ReadFile("./ca/ue/ue_pk_1.pem")
	if err != nil {
		log.Errorln("[RegisterUE]-读取./ca/ue/ue_pk_1.pem文件失败:", err.Error())
		return
	}
	registerParam := &model.RegisterParam{
		MsgId:       "123",
		Mac:         client.MacAddr,
		UeName:      generator.GenRandomUeName(ueType),
		CybertwinId: client.Ctid,
		Uid:         client.Uid,
		Hid:         generator.GenerateHid(client.MacAddr, client.Uid, generator.RandomInt(4, 16)),
		UePublicKey: string(file),
	}
	client.Hid = registerParam.Hid
	codec := protocol.AccessProtocolCodec{}
	marshal, err := proto.Marshal(registerParam) // 序列化请求参数
	if err != nil {
		log.Errorln("[RegisterUE]-序列化registerParam失败:", err.Error())
		return
	}
	encode, err := codec.EncryptEncode(marshal, protocol.TYPE_REGISTER) // 生成协议
	if err != nil {
		log.Errorln("[RegisterUE]-EncryptEncode registerParam失败:", err.Error())
		return
	}

	size, err := conn.Write(encode)
	if err != nil {
		log.Errorln("[RegisterUE]-向注册中心write数据失败:", err.Error())
		return
	}
	log.Infof("[RegisterUE]-发送注册请求，%dbytes数据", size)

	return
}

func (client *RegistryClient) handler_USER_RESPONSE(data []byte, conn gnet.Conn) (err error) {
	userRegisterResp := model.UserRegisterResp{}
	err = proto.Unmarshal(data, &userRegisterResp)
	if err != nil {
		log.Errorln("Unmarshal Failed")
		return
	}
	client.Uid, client.Ctid = userRegisterResp.Uid, userRegisterResp.CybertwinId
	client.RegisterUE(conn)
	return
}

func (client *RegistryClient) handler_RESPONSE(data []byte) (err error) {
	registerResp := model.RegisterResp{}
	err = proto.Unmarshal(data, &registerResp)
	if err != nil {
		log.Errorln("Unmarshal Failed")
		return
	}
	client.Ueid = registerResp.UeId
	return
}

func AccessHello(conn gnet.Conn) (err error) {
	accessHello, err := protocol.CodecUtils.AccessProtocolCodec.Encode(
		[]byte("access hello"), protocol.TYPE_ACCESSHELLO)
	if err != nil {
		return
	}
	_, err = conn.Write(accessHello)
	log.Infoln("[OnOpen]-发送access hello请求")
	return err
}

func (client *ApClient) handler_ACCESSHELLO_RESP(data []byte, conn gnet.Conn) (err error) {
	accessHelloResp := &model.AccessHelloResp{}
	err = proto.Unmarshal(data, accessHelloResp)
	if err != nil {
		log.Errorln("Unmarshal Failed")
		return
	}
	// 日志中进行超长字符串截断，否则太长影响观感
	log.WithFields(
		log.Fields{
			"pk":   stringutils.GetPrefix(string(accessHelloResp.PublicKey), 6) + "...",
			"cert": stringutils.GetPrefix(string(accessHelloResp.Certificate), 6),
		}).Infoln("[OnTraffic]-[handler_ACCESSHELLO_RESP]-成功获取access hello的响应")

	caBlock, _ := pem.Decode(accessHelloResp.Certificate)
	certificate, _ := x509.ParseCertificate(caBlock.Bytes)
	verifyResult := ca.Verify(certificate, "./ca/root/cert.crt")
	if !verifyResult {
		err = errors.New("CA Verification Failed")
		return
	}

	// 存储ap的公钥
	block, _ := pem.Decode(accessHelloResp.PublicKey)
	key, err := x509.ParsePKCS1PublicKey(block.Bytes)
	if err != nil {
		log.Errorln("[OnTraffic]-[handler_ACCESSHELLO_RESP]-解析公钥失败:", err)
		return err
	}
	client.apPublicKey = key

	// 客户端产生随机数r1，然后与hid进行拼接。使用ap的公钥进行加密
	client.r1 = generator.RandomInt(1, 10)
	a := cryptotools.Power(cryptotools.BASE, int64(client.r1)) % cryptotools.MODULUS
	transferData := fmt.Sprintf("%s||%d", client.Hid, a)
	encryptTransferData, err1 := cryptotools.RSA_Encrypt([]byte(transferData), client.apPublicKey)
	if err1 != nil {
		err = err1
		return
	}
	uePrivateKey := cryptotools.RSA_LoadPrivateKey("./ca/ue", "ue_sk_1.pem")
	hash, signature := cryptotools.RSA_Sign([]byte(transferData), uePrivateKey)

	// 生成要发送的数据类型，该数据类型包括hid+r1以及其hash和signature
	request := &model.HidCheckRequest{
		EncryptedData: encryptTransferData,
		Hash:          hash[:],
		Signature:     signature,
	}
	serialRequest, _ := proto.Marshal(request)
	encode, _ := protocol.CodecUtils.AccessProtocolCodec.Encode(serialRequest, protocol.TYPE_HID_CHECK)

	// 发送
	_, err = conn.Write(encode)
	return
}

func (client *ApClient) handler_HID_CHECK_RESP(data []byte, conn gnet.Conn) (err error) {
	resp := &model.HidCheckResp{}
	err = proto.Unmarshal(data, resp)
	if err != nil {
		return
	}
	// 解密ctid+r2
	verify := cryptotools.RSA_SignVerify([32]byte(resp.Hash), resp.Signature, client.apPublicKey)
	if !verify {
		err = errors.New("CA Verification Failed")
		return
	}
	ctidAndR2, err := cryptotools.RSA_Decrypt(resp.EncryptedData, client.uePrivateKey)
	if err != nil {
		return
	}
	splitRes := strings.Split(string(ctidAndR2), stringutils.SEP)
	log.Infof("[OnTraffic]-[handler_HID_CHECK_RESP]-接收到来自ap的消息:%s", ctidAndR2)
	b, err1 := strconv.ParseInt(splitRes[1], 10, 64)
	if err1 != nil {
		err = err1
		return
	}
	client.ck1 = cryptotools.Int64ToBytes(cryptotools.Power(b, int64(client.r1)) % cryptotools.MODULUS)
	client.ck1 = append(client.ck1, client.ck1...)
	log.Infof("[OnTraffic]-[TYPE_HID_CHECK_RESP]-获得ck1密钥:%d, 原料中B=%d, r1 = %d",
		cryptotools.Power(b, int64(client.r1))%cryptotools.MODULUS, b, client.r1)

	// token请求
	tokenRequest := &model.TokenRequest{
		Ctid: splitRes[0],
		Ueid: client.Ueid, // ue自己是知道ueid的
		Uid:  "user_01",   // ue自己也应该知道uid
	}
	tokenRequestBytes, _ := proto.Marshal(tokenRequest)
	encryptTokenRequestBytes, err2 := cryptotools.AES_Encrypt(tokenRequestBytes, client.ck1)
	if err2 != nil {
		err = err2
		return
	}
	encode, err3 := protocol.CodecUtils.AccessProtocolCodec.Encode(encryptTokenRequestBytes, protocol.TYPE_TOKEN_APPLY)
	if err3 != nil {
		err = err3
		return
	}

	_, err = conn.Write(encode)
	return
}

func (client *ApClient) handler_TOKEN_APPLY_RESP(data []byte, conn gnet.Conn) (err error) {
	// 该信息由ck1加密，因此应该由ck1解密
	data, err = cryptotools.AES_Decrypt(data, client.ck1)
	if err != nil {
		return
	}
	resp := &model.TokenResp{}
	err = proto.Unmarshal(data, resp)
	if err != nil {
		return
	}
	client.Token = resp.Token // 存储token
	log.Infoln("[ApClient OnTraffic]-[handler_TOKEN_APPLY_RESP]-获取到随机数r3:", resp.R3)
	// Just for MOCK
	client.Ct_ip = CT_IP
	client.Ct_port = CT_PORT
	log.Infof("[ApClient OnTraffic]-[handler_TOKEN_APPLY_RESP]-获取到孪生体地址: ip = %s, port = %s\n", client.Ct_ip, client.Ct_port)
	client.R3 = int(resp.R3)

	return
}

// handler_ACCESS_CYBERTWIN 当ue连接cybertwin时，在OnOpen中会调用这个方法。该方法向ct传递token（token被ck2加密）
func (client *CybertwinClient) handler_ACCESS_CYBERTWIN(conn gnet.Conn) (err error) {
	codec := protocol.CodecUtils.AccessProtocolCodec
	conn.SetContext(&connContext{})
	msg := &model.PROTO_Access_Cybertwin{
		Token: client.Token,
	}
	serializedMsg, err1 := proto.Marshal(msg)
	if err1 != nil {
		err = err1
		return
	}
	encode, err2 := codec.Encode(serializedMsg, protocol.TYPE_ACCESS_CYBERTWIN)
	if err2 != nil {
		err = err2
		return
	}
	_, err = conn.Write(encode)
	return
}

// handler_ACCESS_CYBERTWIN_RESP 生成ck3，以便于后续对请求进行加密
func (client *CybertwinClient) handler_ACCESS_CYBERTWIN_RESP(data []byte, conn gnet.Conn) (err error) {
	// 该data使用r3进行加密了
	r3Bytes := cryptotools.Int64ToBytes(int64(client.R3))
	key := append(r3Bytes, r3Bytes...)
	resp := &model.PROTO_Cybertwin_Resp_Ue{}
	err = proto.Unmarshal(data, resp)
	if err != nil {
		return
	}

	r4, err := cryptotools.AES_Decrypt(resp.Er4, key)
	if err != nil {
		return
	}
	r4ToInt64, err := cryptotools.Bytes8ToInt64(r4)
	if err != nil {
		return
	}
	log.Infof("[ctClient OnTraffic]-[TYPE_ACCESS_CYBERTWIN_RESP]-成功获取到 r4 = %d, r3 = %d",
		r4ToInt64, client.R3)

	cc := conn.Context().(*connContext)
	cc.ck3 = append(cryptotools.Int64ToBytes(int64(client.R3)), r4...) // r3拼接r4得到ck3
	client.Ck3 = cc.ck3
	log.Infof("[ctClient OnTraffic]-[TYPE_ACCESS_CYBERTWIN_RESP]-生成ck3 = %v", cc.ck3)
	return
}

func (client *CybertwinClient) handler_CYBERTWIN_UE_RESPONSE(data []byte) (rid uint32, err error) {
	respHeader, respData, err := protocol.CodecUtils.ResponseProtocolCodec.Decode(data)
	rid = respHeader.RequestID
	t, isExist := client.Req_map[rid]
	if !isExist {
		log.Warnln("[ctClient OnTraffic]-[handler_CYBERTWIN_UE_RESPONSE]-Received an illegal response")
	}
	log.Infof("[ctClient OnTraffic]-[handler_CYBERTWIN_UE_RESPONSE]-Request %d Time Cost:%v", rid, t.Stop())
	if err != nil {
		return
	}
	log.WithFields(log.Fields{
		"UEID":       stringutils.GetPrefix(client.Ueid, 9),
		"requestID":  respHeader.RequestID,
		"RESP_STATE": respHeader.RESP_STATE,
		"response":   string(respData),
	}).Infof("[ctClient OnTraffic]-[handler_CYBERTWIN_UE_RESPONSE]-服务请求成功获取响应:")

	if client.TimerForReconnect != nil {
		dura := client.TimerForReconnect.Stop()
		log.Infof("[ctClient]-[OnTraffic]-断线重连总时间为:%s", dura)
	}
	// todo: 返回一个ack给cybertwin，包含ueid和requestid
	return
}
